using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize
{
    public unsafe partial struct LStreamReader
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int AssistUnmanagedStruct<T>(out T* ptrHeader) where T : unmanaged =>
            assist->HandleUnmanagedStruct(out ptrHeader); 
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int HandleUnmanagedStruct<T>(out T* ptrHeader) where T : unmanaged =>
            buffer->HandleUnmanagedStruct(out ptrHeader);
        
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ReadBufferSpan<T>(LSerializeContext* context, ref T value, out LStreamBufferSpan span)
        {
            bool result = false;
            span = default;

            var size = sizeof(ValHeader) + sizeof(ulong);
            // Log.Info($"Read buffer span size: {size}, remaining: {assist->Remain}, type: {typeof(T).Name}");
            if (assist->EnsureTail(size))
            {
                assist->HandleUnmanagedStruct(out ValHeader* header);
                assist->HandleUnmanagedStruct(out ulong* instIdPtr);
                // Assert.Check(context->Tag <= header->Tag, "ReadBufferSpan tag not match!");

                if (context->Depth == header->Depth && context->Tag == header->Tag)
                {
                    var instId = (long) *instIdPtr;
                    // Log.Info($"Read buffer span cache: {instId}, {header->Tag}, {context->Tag}");
                    if (LSerializeExtension.CacheObj.TryGetSerializeObj<T>(instId, out var obj))
                    {
                        value = obj;
                        result = false;
                        // Log.Info($"Read [{context->Tag}] buffer [{obj}] at [{instId}].");
                    }
                    else
                    {
                        result = LSerializeExtension.Cache<LStreamBufferSpan>.TryGetCache(instId, out span);
                        // Log.Info($"Read [{context->Tag}] buffer span [{span.Buffer->Remain}] bytes at [{instId}].");
                    }
                }
                else
                {
                    value = default;
                    result = false;
                    assist->Seek(-size, SeekOrigin.Current);
                    // Log.Info($"Read [{context->Tag}] buffer [{header->Tag}], value is default");
                }
            }
            
            context->IncreaseTag();
            return result;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ReadBufferSpan<T>(LSerializeContext* context, ref T[] value, out LStreamBufferSpan span)
        {
            bool result = false;
            span = default;

            var size = sizeof(ValHeader) + sizeof(ulong);
            if (assist->EnsureTail(size))
            {
                assist->HandleUnmanagedStruct(out ValHeader* header);
                assist->HandleUnmanagedStruct(out ulong* instIdPtr);
                // Assert.Check(context->Tag <= header->Tag, "ReadBufferSpan tag not match!");

                if (context->Depth == header->Depth && context->Tag == header->Tag)
                {
                    var instId = (long) *instIdPtr;
                    if (LSerializeExtension.CacheObj.TryGetSerializeObj<T[]>(instId, out var obj))
                    {
                        value = obj;
                        result = false;
                    }
                    else
                    {
                        result = LSerializeExtension.Cache<LStreamBufferSpan>.TryGetCache(instId, out span);
                    }
                }
                else
                {
                    value = default;
                    result = false;
                    assist->Seek(-size, SeekOrigin.Current);
                }
            }

            context->IncreaseTag();
            return result;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ReadBufferSpan<T>(LSerializeContext* context, ref List<T> value, out LStreamBufferSpan span)
        {
            bool result = false;
            span = default;
            
            var size = sizeof(ValHeader) + sizeof(ulong);
            if (assist->EnsureTail(size))
            {
                assist->HandleUnmanagedStruct(out ValHeader* header);
                assist->HandleUnmanagedStruct(out ulong* instIdPtr);
                // Assert.Check(context->Tag <= header->Tag, "ReadBufferSpan tag not match!");

                if (context->Depth == header->Depth && context->Tag == header->Tag)
                {
                    var instId = (long) *instIdPtr;
                    if (LSerializeExtension.CacheObj.TryGetSerializeObj<List<T>>(instId, out var obj))
                    {
                        value = obj;
                        result = false;
                    }
                    else
                    {
                        result = LSerializeExtension.Cache<LStreamBufferSpan>.TryGetCache(instId, out span);
                    } 
                }
                else
                {
                    value = default;
                    result = false;
                    assist->Seek(-size, SeekOrigin.Current);
                }
            }
            
            context->IncreaseTag();
            return result;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ReadBufferSpan<TKey, TValue>(LSerializeContext* context, ref Dictionary<TKey, TValue> value, out LStreamBufferSpan span)
            where TKey : notnull
        {
            bool result = false;
            span = default;
           
            var size = sizeof(ValHeader) + sizeof(ulong);
            if (assist->EnsureTail(size))
            {
                assist->HandleUnmanagedStruct(out ValHeader* header);
                assist->HandleUnmanagedStruct(out ulong* instIdPtr);
                // Assert.Check(context->Tag <= header->Tag, "ReadBufferSpan tag not match!");


                if (context->Depth == header->Depth && context->Tag == header->Tag)
                {
                    var instId = (long) *instIdPtr;
                    if (LSerializeExtension.CacheObj.TryGetSerializeObj<Dictionary<TKey, TValue>>(instId, out var obj))
                    {
                        value = obj;
                        result = false;
                    }
                    else
                    {
                        result = LSerializeExtension.Cache<LStreamBufferSpan>.TryGetCache(instId, out span);
                    }
                }
                else
                {
                    value = default;
                    result = false;
                    assist->Seek(-size, SeekOrigin.Current);
                }
            }

            context->IncreaseTag();
            return result;
        }
    }
}